// 原型链继承
; (function () {
    return
    function Parent() {

        this.name = 'kevin';
        this.friend = ['好友1', '好友2']
    }

    Parent.prototype.getName = function () {
        console.log(this.name);
    }

    function Child() {

    }
    // =================================
    // new Parent();这个实例中的name,friend会被共享
    Child.prototype = new Parent();

    // =================================
    //在创建 Child 的实例时，不能向Parent传参
    var child1 = new Child();


    console.log(child1)//Parent {}
    child1.getName() // kevin

    child1.name = "你好"
    child1.getName() // 你好

    // =================================
    // 共享的问题就是, 在新建一个实例, 修改了原型中的引用类型,所有的实例都都到干扰
    var child2 = new Child();
    console.log(child1.friend) //[ '好友1', '好友2' ]
    child2.friend.push('好友3')
    console.log(child1.friend) //[ '好友1', '好友2', '好友3' ]
})();

// 借用构造函数(经典继承)
; (function () {
    return

    function Parent() {
        this.names = ['kevin', 'daisy'];
    }

    function Child() {
        // 1.避免了引用类型的属性被所有实例共享
        // 因为每一个都会得到自己的属性names

        // 2.可以在 Child 中向 Parent 传参
        Parent.call(this);

        // 缺点
        // =================================
        // 方法都在构造函数中定义，每次创建实例都会创建一遍方法
    }

    var child1 = new Child();

    child1.names.push('yayu');

    console.log(child1.names); // ["kevin", "daisy", "yayu"]

    var child2 = new Child();

    console.log(child2.names); // ["kevin", "daisy"]

})();


// 组合继承(没什么缺点, 会调用构造函数两次)
// 优点：融合原型链继承和构造函数的优点，是 JavaScript 中最常用的继承模式。
; (function () {
    return
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }

    Parent.prototype.getName = function () {
        console.log(this.name)
    }

    function Child(name, age) {

        Parent.call(this, name);

        this.age = age;

    }
    // this.colors = ['red', 'blue', 'green']; 原型里面有一个
    Child.prototype = new Parent();
    // this.colors = ['red', 'blue', 'green']; 实例里面有一个
    var child1 = new Child('kevin', '18');

    child1.colors.push('black');

    console.log(child1.name); // kevin
    console.log(child1.age); // 18
    console.log(child1.colors); // ["red", "blue", "green", "black"]

    var child2 = new Child('daisy', '20');

    console.log(child2.name); // daisy
    console.log(child2.age); // 20
    console.log(child2.colors); // ["red", "blue", "green"]

})();

// 原型式继承
; (function () {
    return
    function createObj(o) {
        function F() { }
        F.prototype = o;
        return new F();
    }
    var person = {
        name: 'kevin',
        friends: ['daisy', 'kelly']
    }

    var person1 = createObj(person);
    var person2 = createObj(person);

    console.log(person1.__proto__ === person); // true

    person1.name = 'person1';
    // 这相当于给实例增加属性, 所以才不影响下面的person2.name
    console.log(person2.name); // kevin
    console.log(person1.friends); // 

    person1.friends.push('taylor'); //[ 'daisy', 'kelly' ]
    console.log(person2.friends); // ["daisy", "kelly", "taylor"]
    console.log(person1.friends); // ["daisy", "kelly", "taylor"]

})();

//寄生式继承
; (function () {
    //return
    function createObj(o) {
        var clone = object.create(o);
        clone.sayName = function () {
            console.log('hi');
        }
        return clone;
        // 缺点：跟借用构造函数模式一样，每次创建对象都会创建一遍方法。
    }

})();

// 寄生组合式继承(这个是最优解)
; (function () {
    //return
    // function Parent (name) {
    //     this.name = name;
    //     this.colors = ['red', 'blue', 'green'];
    // }

    // Parent.prototype.getName = function () {
    //     console.log(this.name)
    // }

    // function Child (name, age) {
    //     Parent.call(this, name);
    //     this.age = age;
    // }

    // Child.prototype = new Parent();

    // var child1 = new Child('kevin', '18');

    // console.log(child1)

    // 组合继承最大的缺点是会调用两次父构造函数。
    // 一次是设置子类型实例的原型的时候：
    // Child.prototype = new Parent();
    // 一次在创建子类型实例的时候：
    // var child1 = new Child('kevin', '18');
    // 在这里，我们又会调用了一次 Parent 构造函数。
    // 所以，在这个例子中，如果我们打印 child1 对象，
    // 我们会发现 Child.prototype 和 child1 都有一个属性为colors，
    // 属性值为['red', 'blue', 'green']。
    // 那么我们该如何精益求精，避免这一次重复调用呢？
    // 如果我们不使用 Child.prototype = new Parent() ，
    // 而是间接的让 Child.prototype 访问到 Parent.prototype 呢？







    // =================================
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }

    Parent.prototype.getName = function () {
        console.log(this.name)
    }

    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }

    // 关键的三步
    var F = function () { };

    F.prototype = Parent.prototype;

    Child.prototype = new F();


    // 关键的三步封装一下=================================
    // function object(o) {
    //     function F() { }
    //     F.prototype = o;
    //     return new F();
    // }

    // function prototype(child, parent) {
    //     var prototype = object(parent.prototype);
    //     // prototype.constructor = child;
    //      使用这样方式,是让这个constructor不可枚举
    //      Object.defineProperty(prototype, "constructor", {
    //          enumerable: false,
    //          value: child
    //      })
    //     child.prototype = prototype;
    // }

    // 当我们使用的时候：
    // prototype(Child, Parent);
    // =================================   


    var child1 = new Child('kevin', '18');

    console.log(child1);
    console.log(child1 instanceof Child);
    console.log(child1 instanceof Parent);
    console.log(Child.prototype.constructor)
})();